////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   statobjmanshadows.cpp
//  Version:     v1.00
//  Created:     2/6/2002 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: Shadow casters/receivers relations
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"

#include "terrain.h"
#include "ObjMan.h"
#include "VisAreas.h"
#include "3dEngine.h"
#include <AABBSV.h>
#include "Vegetation.h"
#include "Brush.h"
#include "LightEntity.h"
#include "ObjectsTree.h"
#include "VoxTerrain.h"

#pragma warning(disable: 4244)

bool IsAABBInsideHull(const SPlaneObject * pHullPlanes, int nPlanesNum, const AABB & aabbBox);

void CObjManager::MakeShadowCastersList(CVisArea*pArea, const AABB & aabbReceiver, int dwAllowedTypes, Vec3 vLightPos, CDLight * pLight, ShadowMapFrustum * pFr, PodArray<SPlaneObject> * pShadowHull, int nEngineFrameID)
{
	FUNCTION_PROFILER_3DENGINE;

	assert(pLight && vLightPos.len()>1); // world space pos required

	pFr->pCastersList->Clear();

  static ICVar*	pVarShadowGenGS = GetConsole()->GetCVar("r_ShadowGenGS");
  bool bUseFrustumTest = false;
#ifndef CONSOLE_CONST_CVAR_MODE
  if (!pVarShadowGenGS || (pVarShadowGenGS && pVarShadowGenGS->GetIVal()!=1))
#endif
    bUseFrustumTest = true;

  CVisArea * pLightArea = pLight->m_pOwner ? (CVisArea*)pLight->m_pOwner->GetEntityVisArea() : NULL;

	if(pArea)
	{
    if(pArea->m_pObjectsTree)
			pArea->m_pObjectsTree->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);

    if(pLightArea)
    {
      // check neighbor sectors and portals if light and object are not in same area
      if(!(pLight->m_Flags & DLF_THIS_AREA_ONLY))
      {
        for(int pp=0; pp<pArea->m_lstConnections.Count(); pp++)
        {
          CVisArea * pN = pArea->m_lstConnections[pp];
          if(pN->m_pObjectsTree)
            pN->m_pObjectsTree->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);

          for(int p=0; p<pN->m_lstConnections.Count(); p++)
          {
            CVisArea * pNN = pN->m_lstConnections[p];
            if(pNN != pLightArea && pNN->m_pObjectsTree)
              pNN->m_pObjectsTree->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);
          }
        }
      }
      else if(!pLightArea->IsPortal())
      { // visit also portals
        for(int p=0; p<pArea->m_lstConnections.Count(); p++)
        {
          CVisArea * pN = pArea->m_lstConnections[p];
          if(pN->m_pObjectsTree)
            pN->m_pObjectsTree->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);
        }
      }
    }
	}
	else
	{	
    PodArray<CTerrainNode*>& lstCastingNodes = m_lstTmpCastingNodes;
		lstCastingNodes.Clear();

		if(pFr->bUseVarianceSM && pFr->bTerrainShadows)
		{
      if(GetCVars()->e_Terrain && Get3DEngine()->m_bShowTerrainSurface && !(GetCVars()->e_VoxTer && GetCVars()->e_Voxel))
      {
			  // get terrain sectors

			  // find all caster sectors
			  GetTerrain()->IntersectWithShadowFrustum(&lstCastingNodes, pFr, GetDefSID());

			  // make list of entities
			  for(int s=0; s<lstCastingNodes.Count(); s++)
			  {
				  IShadowCaster * pNode = (IShadowCaster*)lstCastingNodes[s];
				  pFr->pCastersList->Add(pNode);
			  }
      }

      if(GetCVars()->e_GsmExtendLastLodIncludeObjects)
			{
      for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); nSID++)
        if(Get3DEngine()->IsSegmentSafeToUse(nSID) && Get3DEngine()->m_pObjectsTree[nSID])
						Get3DEngine()->m_pObjectsTree[nSID]->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);
			}
		}
		else
		{
      for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); nSID++)
        if(Get3DEngine()->IsSegmentSafeToUse(nSID) && Get3DEngine()->m_pObjectsTree[nSID])
			    Get3DEngine()->m_pObjectsTree[nSID]->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);

			// check also visareas effected by sun
			CVisAreaManager* pVisAreaManager = GetVisAreaManager();
			if(pVisAreaManager)
      {
				{
					PodArray<CVisArea*> & lstAreas = pVisAreaManager->m_lstVisAreas;
			  for(int i=0; i<lstAreas.Count(); i++)
				  if(lstAreas[i]->IsAffectedByOutLights() && lstAreas[i]->m_pObjectsTree)
					{
						bool bUnused = false;
            if(!bUseFrustumTest || pFr->IntersectAABB(*lstAreas[i]->GetAABBox(), &bUnused))
              if(!pShadowHull || IsAABBInsideHull(pShadowHull->GetElements(), pShadowHull->Count(), *lstAreas[i]->GetAABBox()))
    							lstAreas[i]->m_pObjectsTree->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);
					}
      }
      {
					PodArray<CVisArea*> & lstAreas = pVisAreaManager->m_lstPortals;
        for(int i=0; i<lstAreas.Count(); i++)
          if(lstAreas[i]->IsAffectedByOutLights() && lstAreas[i]->m_pObjectsTree)
					{
						bool bUnused = false;
            if(!bUseFrustumTest || pFr->IntersectAABB(*lstAreas[i]->GetAABBox(), &bUnused))
							if(!pShadowHull || IsAABBInsideHull(pShadowHull->GetElements(), pShadowHull->Count(), *lstAreas[i]->GetAABBox()))
									lstAreas[i]->m_pObjectsTree->FillShadowCastersList(false, pLight, pFr, pShadowHull, bUseFrustumTest, nEngineFrameID);
					}
      }
			}
		
      bool bNeedRenderTerrain = (GetTerrain() && GetCVars()->e_GsmCastFromTerrain && (pLight->m_Flags&DLF_SUN) != 0)
																|| (pLight->m_Flags&DLF_REFLECTIVE_SHADOWMAP) != 0;

      if(bNeedRenderTerrain && GetCVars()->e_Terrain && Get3DEngine()->m_bShowTerrainSurface && !(GetCVars()->e_VoxTer && GetCVars()->e_Voxel))
      {
        // find all caster sectors
        GetTerrain()->IntersectWithShadowFrustum(&lstCastingNodes, pFr, GetDefSID());

        // make list of entities
        for(int s=0; s<lstCastingNodes.Count(); s++)
        {
          CTerrainNode * pNode = (CTerrainNode*)lstCastingNodes[s];

					if(pLight->m_Flags & DLF_SUN)
					{
						if(pNode->m_nGSMFrameId == nEngineFrameID)
							continue;

						if(!pFr->bForSubSurfScattering && bNeedRenderTerrain && pFr->FrustumPlanes[0].IsAABBVisible_EH(pNode->GetBBoxVirtual()) == CULL_INCLUSION)
							pNode->m_nGSMFrameId = nEngineFrameID; // mark as completely present in some LOD 
					}

          pFr->pCastersList->Add(pNode);
        }
      }

      if(m_pVoxTerrain && (GetCVars()->e_VoxTer && GetCVars()->e_Voxel) && !pFr->bForSubSurfScattering && GetCVars()->e_VoxTerShadows)
        m_pVoxTerrain->FillShadowCastersList(pLight, pFr, pShadowHull, bUseFrustumTest, 8);
    }
	}
}

uint64 CObjManager::GetShadowFrustumsList( PodArray<CDLight*> * pAffectingLights, const AABB & aabbReceiver,
																				float fObjDistance, uint32 nDLightMask, bool bIncludeNearFrustums)
{
	FUNCTION_PROFILER_3DENGINE;

  assert(GetCVars()->e_Shadows);

  if(!pAffectingLights || !pAffectingLights->Count())
    return 0;

	const int MAX_MASKED_GSM_LODS_NUM = 4;

	int nExtendedFrustumInUse = 0;
	if(Get3DEngine()->m_bGsmExtendLastLod && GetCVars()->e_Terrain && Get3DEngine()->m_bShowTerrainSurface && !(GetCVars()->e_VoxTer && GetCVars()->e_Voxel))
  	if(nDLightMask & 1)
	    if(CDLight * pLight = pAffectingLights->GetAt(0))
	      if((pLight->m_Flags & DLF_SUN) && (pLight->m_Id>=0))
		      nExtendedFrustumInUse = 1;

	// check for RSM presence
	if(nExtendedFrustumInUse == 0  && GetCVars()->e_GI)
	{
		if(GetCVars()->e_Terrain && Get3DEngine()->m_bShowTerrainSurface && !GetCVars()->e_VoxTer)
			for(int i=0; i<pAffectingLights->Count(); i++)
				if(CDLight * pLight = pAffectingLights->GetAt(0))
					if((pLight->m_Flags & DLF_REFLECTIVE_SHADOWMAP) && (pLight->m_Id>=0))
						nExtendedFrustumInUse = 1;
	}

  C3DEngine * pEng = Get3DEngine();

	// calculate frustums list id
	uint64 nCastersListId = 0;
	int32 nSunID = 0;
	uint32 nFrustums = 0;
	if(bIncludeNearFrustums)
	for(int i=0; i<64 && i<pAffectingLights->Count(); i++)
	{
		CDLight * pLight = pAffectingLights->GetAt(i);
		
    assert(pLight->m_Id < 64);

		bool bSun = (pLight->m_Flags & DLF_SUN) != 0;

		if((pLight->m_Id>=0) && (nDLightMask & (1<<pLight->m_Id)) && (pLight->m_Flags & DLF_CASTSHADOW_MAPS))
		{
			if(CLightEntity::ShadowMapInfo * pSMI = ((CLightEntity *)pLight->m_pOwner)->m_pShadowMapInfo)
			{
				for(int nLod=0; nLod<Get3DEngine()->m_nGsmLodsNum && nLod<MAX_MASKED_GSM_LODS_NUM && pSMI->pGSM[nLod]; nLod++)
				{
					ShadowMapFrustum * pFr = pSMI->pGSM[nLod];
					if(pFr->pCastersList && pFr->pCastersList->Count())
					{	// take GSM Lod's containing receiver bbox inside shadow frustum
						bool bUnused = false;
						if(pFr->IntersectAABB(aabbReceiver, &bUnused))
						{
							if (bSun)
							{
								nSunID = pLight->m_Id;
								nCastersListId |= (uint64(1)<<nLod);
							}
							else
								nCastersListId |= (uint64(1)<<pLight->m_Id);
							nFrustums++;
						}

						// todo: if(nCull == CULL_INCLUSION) break;
					}
				}
			}
		}
	}
	assert(nSunID == 0);

	if(!nExtendedFrustumInUse)
		if(!nCastersListId)
			return 0;

	//TArray<uint32> * pList = stl::find_in_map( pEng->m_FrustumsCache[nExtendedFrustumInUse], nCastersListId, NULL );

	//// fill the list if not ready
	//if(!pList || !pList->Count())
	//{ 
	//	if(!pList)
	//	{
	//		pList = new PodArray<ShadowMapFrustum*>;
	//		pEng->m_FrustumsCache[nExtendedFrustumInUse][nCastersListId] = pList;
	//	}

	//	for(int nBitId=0; nBitId<64 && (uint64(1)<<nBitId) <= nCastersListId; nBitId++)
	//	{
	//		if(nCastersListId & (uint64(1)<<nBitId))
	//		{
	//			int nLightId = nBitId/MAX_MASKED_GSM_LODS_NUM;
	//			int nLod = nBitId - nLightId*MAX_MASKED_GSM_LODS_NUM;

	//			assert(nLightId < pEng->GetRealLightsNum());

	//			CDLight * pLight = (CDLight*)GetRenderer()->EF_Query(EFQ_LightSource, nLightId);

 //       assert(pLight->m_Id>=0);

	//			CLightEntity::ShadowMapInfo * pSMI = ((CLightEntity *)pLight->m_pOwner)->m_pShadowMapInfo;

	//			pList->Add(pSMI->pGSM[nLod]);
	//		}
	//	}

    //Scattering LOD
    //TOFIX:: Currently GSM LOD is used together with terrain shadow LOD only
  //  if(Get3DEngine()->m_nGsmLodsNum < MAX_GSM_LODS_NUM)
  //  {
  //    //CDLight * pLight = pAffectingLights->GetAt(0);
  //    //CLightEntity::ShadowMapInfo * pSMI0 = ((CLightEntity *)pLight->m_pOwner)->m_pShadowMapInfo;
  //    int nScatterLOD = Get3DEngine()->m_nGsmLodsNum; //LOD is just after the last valid GSM LOD
  //    //if(pSMI0 && pSMI0->pGSM[nScatterLOD])
  //    //  pList->Add(pSMI0->pGSM[nScatterLOD]);

		//	assert(nScatterLOD >= 0 && nScatterLOD < 8);
		//	nCastersListId |= (uint64(1)<<nScatterLOD);
  //  }

		//if(nExtendedFrustumInUse && Get3DEngine()->m_nGsmLodsNum <= MAX_GSM_LODS_NUM)
		//{
		//	//CDLight * pLight = pAffectingLights->GetAt(0);
		//	//CLightEntity::ShadowMapInfo * pSMI = ((CLightEntity *)pLight->m_pOwner)->m_pShadowMapInfo;
		//	//if(pSMI && pSMI->pGSM[Get3DEngine()->m_nGsmLodsNum-1])
		//	//pList->Add(pSMI->pGSM[Get3DEngine()->m_nGsmLodsNum-1]);

		//	int nScatterLOD = Get3DEngine()->m_nGsmLodsNum - 1;
		//	assert(nScatterLOD >= 0 && nScatterLOD < 8);
		//	nCastersListId |= (uint64(1)<<nScatterLOD);
		//}

		assert(nCastersListId);
	//}

	//pEng->m_FrustumsCacheUsers[nExtendedFrustumInUse][nCastersListId]++;

	//static uint32 nCombinations = 0;
	//if (++pEng->m_FrustumsCacheUsers[0][nCastersListId] == 1)
	//{
	//	nCombinations++;
	//	CryLog(ToString(nCombinations));
	//}

	return nCastersListId;
}
